package com.cande.loki.exercise.manythread.futuretest;

import java.util.concurrent.*;
import java.util.function.BiConsumer;
import java.util.function.Function;

/**
 * @auther ROCK CODER
 * @DATE 2022/4/8 7:07
 * @Description
 */
public class FutureTest {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        ExecutorService ex = Executors.newFixedThreadPool(10);
        A a = new A();
        B b = new B();
        long starttime = System.currentTimeMillis();

        FutureTask<A> userInfoFutureTask = new FutureTask<>(new Callable<A>() {
            @Override
            public A call() throws Exception {
                return a.getA();
            }
        });
        /*
        execute只能提交Runnable类型的任务，无返回值。submit既可以提交Runnable类型的任务，也可以提交Callable类型的任务，
            会有一个类型为Future的返回值，但当任务类型为Runnable时，返回值为null。
        execute在执行任务时，如果遇到异常会直接抛出，而submit不会直接抛出，只有在使用Future的get方法获取返回值时，才会抛出异常。
         */
//        ex.execute(userInfoFutureTask);
        ex.submit(userInfoFutureTask);
        Thread.sleep(300);
        Future<B> ft = ex.submit(new Callable<B>() {
            @Override
            public B call() throws Exception {
                return b.getB();
            }
        });
        userInfoFutureTask.get();
        ft.get();
        System.out.println("总共用时："+(System.currentTimeMillis()-starttime));

       /**
        *CompletableFuture  学习
        *创建异步任务，一般有supplyAsync（支持返回值）和runAsync（无返回值）两个方法
        */
        CompletableFuture<Void> runsync = CompletableFuture.runAsync(()-> {System.out.println("runsync");
            },ex);
        CompletableFuture<String> supplyasync = CompletableFuture.supplyAsync(()-> {
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            if (false) throw new RuntimeException();
            return "supplyasync" ;},ex);
        CompletableFuture<String> supplyasync1 = CompletableFuture.supplyAsync(()->" supplyasync1");
        //whenComplete 和 whenCompleteAsync 的区别：
        //whenComplete：是执行当前任务的线程执行继续执行 whenComplete 的任务。
        //whenCompleteAsync：是执行把 whenCompleteAsync 这个任务继续提交给线程池来进行执行。

        supplyasync.whenComplete((s, throwable) -> System.out.println(s+"whenComplete"));

        // 任务完成或异常方法完成时执行该方法
        // 如果出现了异常,任务结果为null
        supplyasync.whenCompleteAsync((s, throwable) -> System.out.println(s+"whenComplete"),ex);

        // 出现异常时先执行该方法
        supplyasync.exceptionally(new Function<Throwable, String>() {
                                      @Override
                                      public String apply(Throwable t) {
                                          System.out.println("执行失败：" + t.getMessage());
                                          return "异常xxxx";
                                      }
                                  });


        // thenApply 当一个线程依赖另一个线程时, 可以使用 thenApply 方法来把这两个线程串行化 第二个任务依赖第一个任务的结果
        supplyasync.thenApply(s -> {
            System.out.println(s+"thenApply");
            return s+"thenApply";});


        // handle 方法
        //可以处理异常的任务。thenApply 只可以执行正常的任务，任务出现异常则不执行 thenApply 方法。
        supplyasync.handle((m,t)->{
            if (t==null){
                System.out.println("handle" + m);
            }else {
                System.out.println("handle" + t +"error");
            }
            return "handle";
        });

        //thenRun 方法
        //不同的是上个任务处理完成后，并不会把计算的结果传给 thenRun 方法。只是处理玩任务后，执行 thenRun 的后续操作。
        supplyasync.thenRun(()->{
            System.out.println("i`m run");
        });

        //thenCombine 合并任务
        //thenCombine 会把 两个 CompletionStage 的任务都执行完成后，把两个任务的结果一块交给 thenCombine 来处
        System.out.println(
        supplyasync1.thenCombine(supplyasync, (z,v)-> z+""+v).get()
        );

        //thenAcceptBoth
        // 这个分别 上面thenCombine的区别就是没有return
        supplyasync1.thenAcceptBoth(supplyasync, (z,v)-> {System.out.println( "thenCombine"+z+""+v);});

        //applyToEither 方法
        //谁执行返回的结果快，我就用那个CompletionStage的结果进行下一步的转化操作。
        System.out.println(
        supplyasync1.applyToEither(supplyasync, s -> "applyToEither"+s).get()
        );

        //acceptEither 方法
        //谁执行返回的结果快，我就用那个CompletionStage的结果进行下一步的消耗操作。
        supplyasync1.acceptEither(supplyasync, s -> System.out.println( "applyToEither"+s));

        //runAfterEither 方法
        //两个CompletionStage，任何一个完成了都会执行下一步的操作（Runnable）

        //runAfterBoth
        //两个CompletionStage，都完成了计算才会执行下一步的操作（Runnable）

        //thenCompose 方法
        //thenCompose 方法允许你对两个 CompletionStage 进行流水线操作，第一个操作完成时，将其结果作为参数传递给第二个操作。
        supplyasync.thenCompose(x-> CompletableFuture.supplyAsync(()->x+"thenCompose"));



        //get 和 join的区别是 get抛出的是checked exception，必须捕获或者抛出 ，而join抛出的话是运行时异常
        //这个方法会阻塞
        System.out.println(runsync.get());
        System.out.println(runsync.join());
        System.out.println(supplyasync.get());
        ex.shutdown();
    }
}

class A {
    A getA() throws InterruptedException {
        Thread.sleep(1000);
        return new A();
    }
}

class B {
    B getB() throws InterruptedException {
        Thread.sleep(500);
        return new B();
    }
}
